﻿using System;
using System.Collections.Generic;
using System.Threading;
using Lucene.Linq.Utility;

namespace Lucene.Linq.Expressions
{
    /// <summary>
    /// Thread safe type cache for generic types
    /// </summary>
    /// <typeparam name="T">The type to cache (must be a class)</typeparam>
    internal class TypeCache<T>
        where T : class
    {
        protected readonly Dictionary<Type, T> Dictionary;
        protected readonly ReaderWriterLockSlim DictionaryLock;

        protected TypeCache()
        {
            Dictionary = new Dictionary<Type, T>();
            DictionaryLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
        }

        internal T this[Type t]
        {
            get
            {
                if (t == null)
                    return DefaultInstance();

                using (DictionaryLock.UpgradableReadLock())
                {
                    if (Dictionary.ContainsKey(t))
                    {
                        return Dictionary[t];
                    }
                    using (DictionaryLock.WriteLock())
                    {
                        T item = ActivateInstance(t);
                        Dictionary.Add(t, item);
                        return item;
                    }
                }
            }

            set
            {
                using (DictionaryLock.WriteLock())
                {
                    if (value == null)
                        throw new ArgumentException("value");

                    if (Dictionary.ContainsKey(t))
                    {
                        Dictionary[t] = value;
                    }
                    else
                    {
                        Dictionary.Add(t, value);
                    }
                }
            }
        }

        protected virtual T ActivateInstance(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }

            object result = Activator.CreateInstance(type);
            return result as T;
        }

        protected virtual T DefaultInstance()
        {
            return default(T);
        }
    }
}